package edu.fh.salzburg.its.opsystems.newfs.implementation;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;

import edu.fh.salzburg.its.opsystems.newfs.definition.FileAttributes;
import edu.fh.salzburg.its.opsystems.newfs.definition.NewFS;
import edu.fh.salzburg.its.opsystems.newfs.definition.NewFSConstants;
import edu.fh.salzburg.its.opsystems.newfs.definition.NewFSDirectory;
import edu.fh.salzburg.its.opsystems.newfs.definition.NewFSElement;
import edu.fh.salzburg.its.opsystems.newfs.exception.NewFSException;
import edu.fh.salzburg.its.opsystems.newfs.definition.NewFSFile;

public class MBRAFileSystem implements NewFS {

	// Datafields
	private byte[] disk;				// represents the space on the disk
	private NewFSFat[] fat;				// represents the File allocation table

	// attributes
	private MBRADirectory root;			// the root directory


	// constructors
	public MBRAFileSystem() {
		format();
	}

	/**
	 * checks if the file exists in the directory tree. <br>
	 * If not the file is added to the directory specified by file.path.
	 */
	@Override
	public int write(NewFSFile file, Byte[] data) throws NewFSException {
		String[] path = file.getPath().split(NewFSConstants.PATH_SEPARATOR);
		String name = file.getName();

		String dirname = new String();
		String[] dirpath = (path.length > 1) ? new String[path.length-1] : path;
		if (path[0].equals("")) {
			dirname = "root";
		}
		else {
			dirname = path[path.length-1];
			for (int i=0; i<path.length-1; i++) {
				dirpath[i] = path[i];
			}
		}
		MBRADirectory dir = getDirFromDisc(dirpath, dirname);
		if (dir.containsElement(name)) {
			throw new NewFSException("File already exists.");
		}

		// initialize File
		MBRAFile f = new MBRAFile(name);
		f.setData(data);
		f.setPath(path);
		f.setDatesNow();
		f.setSize(data.length);
		f.setAttributes(file.getAttributes());

		// select clusters for saving the file
		int neededClusters = 1 + data.length / NewFSConstants.MAX_DATA_SIZE_IN_BYTES;
		int[] clusters = new int[neededClusters];
		int actcluster = 0; 
		for (int i=0; i<neededClusters; i++) {
			clusters[i] = getFreeCluster(actcluster);
			actcluster = clusters[i]+1;
		}
		f.setStartCluster(clusters[0]);
		// writing the file
		for (int i=0; i<neededClusters; i++) {
			updateClusterFile(clusters[i], f, i*NewFSConstants.MAX_DATA_SIZE_IN_BYTES);
			if (i == neededClusters-1) {
				fat[clusters[i]].setLast();
			}
			else {
				fat[clusters[i]].setEntry(clusters[i+1]);
			}
			updateDiskFAT(clusters[i]);
		}
		// update directory entry
		Vector<Integer> usedClusters = getUsedClusters(dir.getDirEntry());
		dir.addElement(f);
		neededClusters = 1 + dir.getSize() / NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER;
		if (usedClusters.size() < neededClusters) {
			usedClusters.add(getFreeCluster(0));
		}
		ArrayList<String> dirs = new ArrayList<String>();
		dirs.addAll(dir.getDirContent().keySet());
		for (int i=0; i<neededClusters; i++) {
			int upindex = (i == neededClusters-1) ? dirs.size() : (i+1)*NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER-1;
			List<String> dtw = dirs.subList(i*NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER, upindex);
			updateClusterDirectory(usedClusters.get(i), dir, dtw);
			if (i == neededClusters-1) {
				fat[usedClusters.get(i)].setLast();
			}
			else {
				fat[usedClusters.get(i)].setEntry(usedClusters.get(i+1));
			}
			updateDiskFAT(usedClusters.get(i));
		}
		return data.length;
	}

	/**
	 * reads a file from the directory specified by file.path.
	 * if the file exists, the data is returned as a byte array.
	 */
	@Override
	public Byte[] read(NewFSFile file) throws NewFSException {
		String[] path = file.getPath().split(NewFSConstants.PATH_SEPARATOR);
		String name = file.getName();
		MBRAFile f = getFileFromDisc(path, name);

		return f.getByteArray();
	}

	/**
	 * returns a list of all Directory entries in the specified directory.
	 */
	@Override
	public List<NewFSElement> getDirectoryContents(NewFSDirectory directory) throws NewFSException {
		String[] path = directory.getPath().split(NewFSConstants.PATH_SEPARATOR);
		String name = directory.getName();
		ArrayList<NewFSElement> list = new ArrayList<NewFSElement>();

		// read dir from disc
		MBRADirectory dir = getDirFromDisc(path, name);
		HashMap<String, NewFSDirEntry> cont = new HashMap<String, NewFSDirEntry>();
		cont = dir.getDirContent();

		if (cont.isEmpty()) {
			return list;
		}
		for (NewFSDirEntry ent : cont.values()) {
			if (ent.getAttributes().contains(FileAttributes.DIRECTORY)) {
				NewFSDirectory d = readDirectoryFromDisk(ent);
				list.add(d);
			}
			else {
				MBRAFile f = readFileFromDisk(ent);
				list.add(f);
			}
		}
		return list;
	}

	/**
	 * creates a sub directory.
	 * the parent directory is specified by the path variable of the directory.
	 * if empty the parent directory is the root directory.
	 */
	@Override
	public void createDirectory(NewFSDirectory directory) throws NewFSException {
		String[] path = directory.getPath().split(NewFSConstants.PATH_SEPARATOR);
		String name = directory.getName();

		// check if path exists
		String dirname = new String();
		String[] dirpath = (path.length > 1) ? new String[path.length-1] : path;
		if (path[0].equals("")) {
			dirname = "root";
		}
		else {
			dirname = path[path.length-1];
			for (int i=0; i<path.length-1; i++) {
				dirpath[i] = path[i];
			}
		}
		MBRADirectory dir = getDirFromDisc(dirpath, dirname);
		boolean c = dir.containsElement(name);
		if (c) {
			throw new NewFSException("directory already exists.");
		}

		// initialize new Directory
		MBRADirectory d = new MBRADirectory(name);
		d.setPath(path);
		d.setDatesNow();
		d.setSize(0);
		EnumSet<FileAttributes> at = directory.getAttributes();
		at.addAll(d.getAttributes());
		d.setAttributes(at);
		

		// update directory entry
		int cst = getFreeCluster(0);
		d.setStartCluster(cst);
		updateClusterDirectory(cst, d, new ArrayList<String>());
		fat[cst].setLast();
		updateDiskFAT(cst);

		// update parent directory entry
		Vector<Integer> usedClusters = getUsedClusters(dir.getDirEntry());
		dir.addElement(d);
		int neededClusters = 1 + dir.getSize() / NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER;
		if (usedClusters.size() < neededClusters) {
			usedClusters.add(getFreeCluster(0));
		}
		ArrayList<String> dirs = new ArrayList<String>();
		dirs.addAll(dir.getDirContent().keySet());
		for (int i=0; i<neededClusters; i++) {
			int upindex = (i == neededClusters-1) ? dirs.size() : (i+1)*NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER-1;
			List<String> dtw = dirs.subList(i*NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER, upindex);
			updateClusterDirectory(usedClusters.get(i), dir, dtw);
			if (i == neededClusters-1) {
				fat[usedClusters.get(i)].setLast();
			}
			else {
				fat[usedClusters.get(i)].setEntry(usedClusters.get(i+1));
			}
			updateDiskFAT(usedClusters.get(i));
		}
	}

	/**
	 * deletes the specified directory from the disc.
	 * the directory must be empty.
	 */
	@Override
	public void deleteDirectory(NewFSDirectory directory) throws NewFSException {
		String[] path = directory.getPath().split(NewFSConstants.PATH_SEPARATOR);
		String name = directory.getName();

		MBRADirectory dir = getDirFromDisc(path, name);
		
		if (dir.getSize() != 0) {
			throw new NewFSException("directory is not empty");
		}
		else {
			// update parent
			String dirname = new String();
			String[] dirpath = (path.length > 1) ? new String[path.length-1] : path;
			if (path[0].equals("")) {
				dirname = "root";
			}
			else {
				dirname = path[path.length-1];
				for (int i=0; i<path.length-1; i++) {
					dirpath[i] = path[i];
				}
			}
			MBRADirectory parent = getDirFromDisc(dirpath, dirname);

			// update directory entry
			Vector<Integer> usedClusters = getUsedClusters(parent.getDirEntry());
			parent.removeElement(dir.getDirEntry());
			int neededClusters = 1 + parent.getSize() / NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER;
			if (usedClusters.size() > neededClusters) {
				fat[usedClusters.get(neededClusters-1)].setFree();
				updateDiskFAT(usedClusters.get(neededClusters-1));
				usedClusters.remove(neededClusters-1);
			}
			ArrayList<String> dirs = new ArrayList<String>();
			dirs.addAll(parent.getDirContent().keySet());
			for (int i=0; i<neededClusters; i++) {
				int upindex = (i == neededClusters-1) ? dirs.size() : (i+1)*NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER-1;
				List<String> dtw = dirs.subList(i*NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER, upindex);
				updateClusterDirectory(usedClusters.get(i), parent, dtw);
				if (i == neededClusters-1) {
					fat[usedClusters.get(i)].setLast();
				}
				else {
					fat[usedClusters.get(i)].setEntry(usedClusters.get(i+1));
				}
				updateDiskFAT(usedClusters.get(i));
			}
		}
	}

	/**
	 * deletes the specified file from the disc.
	 */
	@Override
	public void deleteFile(NewFSFile file) throws NewFSException {
		String[] path = file.getPath().split(NewFSConstants.PATH_SEPARATOR);
		String name = file.getName();

		// check if file exists
		MBRADirectory dir = root;
		if (path[0].length() > 0) {
			for (int i=0; i<path.length; i++) {
				if (dir.containsElement(path[i])) {
					dir = readDirectoryFromDisk(dir.getElement(path[i]));
				}
				else {
					throw new NewFSException("directory does not exist.");
				}
			}
		}
		if (!dir.containsElement(name)) {
			throw new NewFSException("File does not exists.");
		}
		
		// delete entry in FAT
		NewFSDirEntry de = dir.getElement(name);
		Vector<Integer> clusters = getUsedClusters(de);
		for(int i : clusters) {
			fat[i].setFree();
			updateDiskFAT(i);
		}
		
		// remove from directory
		Vector<Integer> usedClusters = getUsedClusters(dir.getDirEntry());
		dir.removeElement(de);
		int neededClusters = 1 + dir.getSize() / NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER;
		if (usedClusters.size() < neededClusters) {
			usedClusters.add(getFreeCluster(0));
		}
		ArrayList<String> dirs = new ArrayList<String>();
		dirs.addAll(dir.getDirContent().keySet());
		for (int i=0; i<neededClusters; i++) {
			int upindex = (i == neededClusters-1) ? dirs.size() : (i+1)*NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER-1;
			List<String> dtw = dirs.subList(i*NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER, upindex);
			updateClusterDirectory(usedClusters.get(i), dir, dtw);
			if (i == neededClusters-1) {
				fat[usedClusters.get(i)].setLast();
			}
			else {
				fat[usedClusters.get(i)].setEntry(usedClusters.get(i+1));
			}
			updateDiskFAT(usedClusters.get(i));
		}
	}

	/**
	 * lists the FileAttributes of the specified NewFSElement.
	 */
	@Override
	public List<String> getAttributes(NewFSElement element) throws NewFSException {
		String[] path = element.getPath().split(NewFSConstants.PATH_SEPARATOR);
		String name = element.getName();

		// check if exists
		MBRADirectory dir = root;
		NewFSDirEntry de = new NewFSDirEntry();
		for (int i=0; i<path.length; i++) {
			if (i == path.length-1) {
				if (dir.containsElement(name)) {
					de = dir.getElement(name);
				}
				else {
					throw new NewFSException("file not found: " + name);
				}
			}
			else {
				dir = readDirectoryFromDisk(dir.getElement(path[i]));
			}
		}
		ArrayList<String> list = new ArrayList<String>();
		for (FileAttributes fa : de.getAttributes()) {
			list.add(fa.name());
		}
		return list;
	}

	@Override
	public void destroy(int byteAdress) throws NewFSException {
		if (byteAdress > NewFS.STORAGE_SIZE_IN_BYTES) {
			throw new NewFSException("address is outside the disc");
		}
		disk[byteAdress] = 0x00;

	}

	@Override
	public void writeFsToRealFile(File realFileOnDisk) throws NewFSException {
		try {
			
			FileOutputStream fos = new FileOutputStream(realFileOnDisk);
			fos.write(disk);
			fos.close();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	@Override
	public void initializeFromFile(File realFileOnDisk) throws NewFSException {

		
		FileInputStream fis;
		try {
			fis = new FileInputStream(realFileOnDisk);
			if (fis.read(disk, 0, NewFS.STORAGE_SIZE_IN_BYTES) != STORAGE_SIZE_IN_BYTES) {
				throw new NewFSException("file is corrupt.");
			}
			fis.close();
		}
		catch (FileNotFoundException e){
			throw new NewFSException("file not found.");
		} catch (IOException e) {
			e.printStackTrace();
		}
		// FAT auslesen
		for (int a=0; a<NewFSConstants.NROFCLUSTERS; a+=2) {
			int actbyte = 3*a/2;
			int b1 = (disk[actbyte+1] & 0xFF) >> 4;
			int b2 = disk[actbyte+2] & 0xFF;
			b1 |= (disk[actbyte] & 0xFF) << 4;
			b2 |= (disk[actbyte+1] << 8) & 0xFFF;
			fat[a].setEntry(b1);
			fat[a+1].setEntry(b2);
		}
		
		// update root directory
		NewFSDirEntry de = getMainEntryFromCluster(0);
		root = readDirectoryFromDisk(de);
	}

	@Override
	public List<String> checkFilesystem() throws NewFSException {
		// get used clusters
		ArrayList<NewFSDirEntry> elements = new ArrayList<NewFSDirEntry>();
		for (int i=0; i<fat.length; i++) {
			if (fat[i].getEntry() == NewFSFat.LAST) {
				NewFSDirEntry ne = getMainEntryFromCluster(i);
				elements.add(ne);
			}
		}

		ArrayList<String> ret = new ArrayList<String>();

		// list data and used clusters
		ret.add("size of disc........." + NewFS.STORAGE_SIZE_IN_BYTES);
		ret.add("nr of clusters......." + NewFSConstants.NROFCLUSTERS);
		int nrOfUsedClusters = 0;
		for (int i=0; i<fat.length; i++) {
			if (fat[i].getEntry() != NewFSFat.FREE) {
				nrOfUsedClusters++;
			}
		}
		ret.add("clusters in use......" + nrOfUsedClusters);
		ret.add("__________________________________");
		
		ret.add("Found following Files:");
		for (NewFSDirEntry de : elements) {
			ret.add(de.toString());
		}
		return ret;
	}
	
	// methods
	private void format() {
		disk = new byte[NewFS.STORAGE_SIZE_IN_BYTES];
		fat = new NewFSFat[NewFSConstants.NROFCLUSTERS];
		root = new MBRADirectory("root");

		// initialize FAT and Clusters
		for (int i=0; i<NewFSConstants.NROFCLUSTERS; i++) {
			fat[i] = new NewFSFat();
		}
		// set FAT and root
		fat[0].setLast();
		updateDiskFAT(0);

		root = new MBRADirectory("root");
		root.setAttributes(EnumSet.of(FileAttributes.DIRECTORY, FileAttributes.SYSTEMFILE, FileAttributes.WRITEPROTECT));
		root.setDatesNow();
		root.setIdentifer("root");
		root.setSize(0);
		root.setStartCluster(0);
		
		

		// update disk
		
	}
	private void updateDiskFAT(int index) {
		int entry = fat[index].getEntry();
		int diskind = (int)(index*1.5);
		
		if (index % 2 == 0) {
			byte b = (byte)(this.disk[diskind+1] & 0x0F);
			disk[diskind] = (byte)(entry >> 4);
			disk[diskind+1] = (byte)(b | entry << 4);
		}
		else {
			byte b = (byte)(this.disk[diskind] & 0xF0);
			disk[diskind] = (byte)((entry >> 8) | b);
			disk[diskind+1] = (byte)(entry & 0xFF);
		}
	}

	private void updateClusterDirectory(int index, MBRADirectory dir, List<String> dirsToWrite) {
		byte[] mainEntry = dir.getDirEntry().toByteArray();

		int offset = NewFSConstants.FAT_SIZE_IN_BYTES + index * NewFSConstants.CLUSTER_SIZE_IN_BYTES;

		// write main entry
		for (int i=offset; i<mainEntry.length+offset; i++) {
			disk[i] = mainEntry[i-offset];
		}
		//offset += NewFSConstants.FSELEMENT_ENTRY_SIZE_IN_BYTES;
		// write content
		for (String d : dirsToWrite) {
			NewFSDirEntry ent = dir.getElement(d);
			byte[] data = ent.toByteArray();
			offset += NewFSConstants.FSELEMENT_ENTRY_SIZE_IN_BYTES;
			for (int i=offset; i<data.length+offset; i++) {
				disk[i] = data[i-offset];
			}
		}
	}
	private void updateClusterFile(int index, MBRAFile fil, int dataOffset) {
		byte[] mainEntry = fil.getDirEntry().toByteArray();
		int siz = NewFSConstants.FSELEMENT_ENTRY_SIZE_IN_BYTES;

		int offset = NewFSConstants.FAT_SIZE_IN_BYTES + index * NewFSConstants.CLUSTER_SIZE_IN_BYTES;
		// write main entry
		for (int i=offset; i<siz+offset; i++) {
			disk[i] = mainEntry[i-offset];
		}
		
		// write content
		offset += mainEntry.length;
		int length = fil.getData().length - dataOffset;
		for (int i=offset; i<(offset + length); i++) {
			disk[i] = fil.getData()[dataOffset + i - offset];
		}
	}
	
	
	private int getFreeCluster(int offset) throws NewFSException {
		for (int i=offset; i<this.fat.length; i++) {
			if (fat[i].getEntry() == NewFSFat.FREE)
				return i;
		}
		throw new NewFSException("Disk is full, no more free FAT Entrys available.");
	}
	
	private MBRAFile readFileFromDisk(NewFSDirEntry dent) {

		MBRAFile f = new MBRAFile(dent);
		byte[] data = new byte[f.getSize()];
		// determine the used clusters for the specified file.
		Vector<Integer> clusters = getUsedClusters(dent);
		
		// read each cluster from the disk
		for (int i=0; i<clusters.size(); i++ ) {
			int offset = NewFSConstants.FAT_SIZE_IN_BYTES + clusters.get(i) * NewFSConstants.CLUSTER_SIZE_IN_BYTES;

			// set data region
			offset += NewFSConstants.FSELEMENT_ENTRY_SIZE_IN_BYTES;
			int dataOffset = i*NewFSConstants.MAX_DATA_SIZE_IN_BYTES;	
			int length = f.getSize() - dataOffset;
			// read content
			for (int a=offset; a<(offset + length); a++) {
				data[dataOffset+a-offset] = disk[a];
			}
		}
		f.setData(data);
		return f;
		
	}

	private MBRADirectory readDirectoryFromDisk(NewFSDirEntry dent) {

		MBRADirectory d = new MBRADirectory(dent);
		byte[] data = new byte[NewFSConstants.FSELEMENT_ENTRY_SIZE_IN_BYTES];
		// determine the used clusters for the specified file.
		Vector<Integer> clusters = getUsedClusters(dent);
		try {
			d.setDirEntry(getMainEntryFromCluster(clusters.get(0)));
		} catch (NewFSException e1) {
			e1.printStackTrace();
		}
		
		// read each cluster from the disk
		for (int i=0; i<clusters.size(); i++ ) {
			int offset = NewFSConstants.FAT_SIZE_IN_BYTES;
			offset += NewFSConstants.CLUSTER_SIZE_IN_BYTES * clusters.get(i);
			
			offset += NewFSConstants.FSELEMENT_ENTRY_SIZE_IN_BYTES;
			int nrOfEntries = d.getSize() - i*NewFSConstants.MAX_DIRENTRIES_PER_CLUSTER;

			// read each single Directory Entry of this cluster and add to d
			for (int dentr=0; dentr<nrOfEntries; dentr++) {
				int startEntry = offset + dentr * NewFSConstants.FSELEMENT_ENTRY_SIZE_IN_BYTES;
				for (int a=startEntry; a<(startEntry + NewFSConstants.FSELEMENT_ENTRY_SIZE_IN_BYTES); a++) {
					data[a-startEntry] = disk[a];
				}
				// create DirectoryEntry from byte[]
				NewFSDirEntry newDirEntr = new NewFSDirEntry();
				try {
					newDirEntr.setFromByteArray(data);
				} catch (NewFSException e) {
					e.printStackTrace();
				}
				
				// check if is directory or file and add to directory
				if (newDirEntr.getAttributes().contains(FileAttributes.DIRECTORY)) {
					MBRADirectory nd = new MBRADirectory(newDirEntr);
					try {
						d.addElement(nd);
					} catch (NewFSException e) {
						e.printStackTrace();
					}
				}
				else {
					MBRAFile nf = new MBRAFile(newDirEntr);
					try {
						d.addElement(nf);
					} catch (NewFSException e) {
						e.printStackTrace();
					}
				}
			}
		}
		return d;
	}
	
	private Vector<Integer> getUsedClusters(NewFSDirEntry d) {
		Vector<Integer> clusters = new Vector<Integer>();
		clusters.add(d.getStartCluster());
		int nrOfClusters = 1;
		while (fat[clusters.get(nrOfClusters-1)].getEntry() != NewFSFat.LAST) {
			clusters.add(fat[clusters.get(nrOfClusters-1)].getEntry());
			nrOfClusters++;
		}
		return clusters;
	}
	
	private NewFSDirEntry getMainEntryFromCluster(int index) throws NewFSException {
		NewFSDirEntry de = new NewFSDirEntry();
		byte[] data = new byte[NewFSConstants.FSELEMENT_ENTRY_SIZE_IN_BYTES];
		
		int offset = NewFSConstants.FAT_SIZE_IN_BYTES + index*NewFSConstants.CLUSTER_SIZE_IN_BYTES;
		for(int i=offset; i<offset+data.length; i++) {
			data[i-offset] = disk[i];
		}
		de.setFromByteArray(data);
		return de;
	}
	
	private MBRADirectory getDirFromDisc(String[] path, String name) throws NewFSException {
		// check if file exists
		if (path[0].equals("") && name.equals("root")) {
			return root;
		}
		MBRADirectory dir = root;
		for (int i=0; i<path.length; i++) {
			if (i == path.length-1) {
				if (dir.containsElement(name)) {
					NewFSDirEntry de = dir.getElement(name);
					if (!de.getAttributes().contains(FileAttributes.DIRECTORY)) {
						throw new NewFSException("not a directory.");
					}
					else {
						dir = readDirectoryFromDisk(de);
					}
				}
				else {
					throw new NewFSException("directory not found.");
				}
			}
			else {
				if (dir.containsElement(path[i])) {
					dir = readDirectoryFromDisk(dir.getElement(path[i]));
				}
				else {
					throw new NewFSException("path not found.");
				}
			}
		}
		return dir;
	}

	private MBRAFile getFileFromDisc(String[] path, String name) throws NewFSException {
		// check if file exists
		String dirname = new String();
		String[] dirpath = (path.length > 1) ? new String[path.length-1] : path;
		if (path[0].equals("")) {
			dirname = "root";
		}
		else {
			dirname = path[path.length-1];
			for (int i=0; i<path.length-1; i++) {
				dirpath[i] = path[i];
			}
		}
		MBRADirectory dir = getDirFromDisc(dirpath, dirname);
		MBRAFile f = new MBRAFile(name);
		if (dir.containsElement(name)) {
			NewFSDirEntry de = dir.getElement(name);
			if (de.getAttributes().contains(FileAttributes.DIRECTORY)) {
				throw new NewFSException("not a file.");
			}
			else {
				f = readFileFromDisk(de);
			}
		}
		else {
			throw new NewFSException("file not found.");
		}
		return f;
	}
}
